package com.silverduner.common;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.time.DurationFormatUtils;

/**
 * Enhancement of apache's StopWatch
 * @author Yuwen Sun
 *
 */
public class StopWatch {

    // running states
	private static enum RunState{STATE_UNSTARTED,STATE_RUNNING,STATE_STOPPED,STATE_SUSPENDED};

    // split state
	private static enum SplitState{STATE_UNSPLIT,STATE_SPLIT};

    /**
     * The current running state of the StopWatch.
     */
    private RunState runningState = RunState.STATE_UNSTARTED;

    /**
     * Whether the stopwatch has a split time recorded.
     */
    private SplitState splitState = SplitState.STATE_UNSPLIT;

    private long startTime = -1;
    private long stopTime = -1;
    
    private Map<String,Long> splitTime = new HashMap<String,Long>();

    public StopWatch() {
        super();
    }

    public void start() {
        if (this.runningState == RunState.STATE_STOPPED) {
            throw new IllegalStateException("Stopwatch must be reset before being restarted. ");
        }
        if (this.runningState != RunState.STATE_UNSTARTED) {
            throw new IllegalStateException("Stopwatch already started. ");
        }
        this.stopTime = -1;
        this.startTime = System.currentTimeMillis();
        this.runningState = RunState.STATE_RUNNING;
    }

    public void stop() {
        if (this.runningState != RunState.STATE_RUNNING && this.runningState != RunState.STATE_SUSPENDED) {
            throw new IllegalStateException("Stopwatch is not running. ");
        }
        if (this.runningState == RunState.STATE_RUNNING) {
            this.stopTime = System.currentTimeMillis();
        }
        this.runningState = RunState.STATE_STOPPED;
    }

    public void reset() {
        this.runningState = RunState.STATE_UNSTARTED;
        this.splitState = SplitState.STATE_UNSPLIT;
        this.startTime = -1;
        this.stopTime = -1;
        this.splitTime.clear();
    }

    public void split(String identifier) {
        if (this.runningState != RunState.STATE_RUNNING) {
            throw new IllegalStateException("Stopwatch is not running. ");
        }
        this.splitTime.put(identifier, System.currentTimeMillis());
        this.splitState = SplitState.STATE_SPLIT;
    }

    public void unsplit(String identifier) {
        if (this.splitState != SplitState.STATE_SPLIT) {
            throw new IllegalStateException("Stopwatch has not been split. ");
        }
        this.splitTime.remove(identifier);
        this.splitState = SplitState.STATE_UNSPLIT;
    }

    public void suspend() {
        if (this.runningState != RunState.STATE_RUNNING) {
            throw new IllegalStateException("Stopwatch must be running to suspend. ");
        }
        this.stopTime = System.currentTimeMillis();
        this.runningState = RunState.STATE_SUSPENDED;
    }

    public void resume() {
        if (this.runningState != RunState.STATE_SUSPENDED) {
            throw new IllegalStateException("Stopwatch must be suspended to resume. ");
        }
        this.startTime += (System.currentTimeMillis() - this.stopTime);
        this.stopTime = -1;
        this.runningState = RunState.STATE_RUNNING;
    }

    public long getDuration() {
        if (this.runningState == RunState.STATE_STOPPED || this.runningState == RunState.STATE_SUSPENDED) {
            return this.stopTime - this.startTime;
        } else if (this.runningState == RunState.STATE_UNSTARTED) {
            return 0;
        } else if (this.runningState == RunState.STATE_RUNNING) {
            return System.currentTimeMillis() - this.startTime;
        }
        throw new RuntimeException("Illegal running state has occured. ");
    }

    public long getSplitDuration(String identifier) {
        if (this.splitState != SplitState.STATE_SPLIT) {
            throw new IllegalStateException("Stopwatch must be split to get the split time. ");
        }
        return this.splitTime.get(identifier) - this.startTime;
    }

    public Date getStart() {
        if (this.runningState == RunState.STATE_UNSTARTED) {
            throw new IllegalStateException("Stopwatch has not been started");
        }
        return new Date(this.startTime);
    }

    public Date getStop() {
        if (this.runningState == RunState.STATE_UNSTARTED) {
            throw new IllegalStateException("Stopwatch has not been started");
        }
        return new Date(this.stopTime);
    }

    public Date getSplit(String identifier) {
        if (this.runningState == RunState.STATE_UNSTARTED) {
            throw new IllegalStateException("Stopwatch has not been started");
        }
        return new Date(this.splitTime.get(identifier));
    }
    
    public String getDurationString(String format){
    	return DurationFormatUtils.formatDuration(getDuration(), format);
    }
 
    public String getDurationString(){
    	return DurationFormatUtils.formatDurationHMS(getDuration());
    }

    public String getSplitDurationString(String identifier,String format) {
        return DurationFormatUtils.formatDuration(getSplitDuration(identifier), format);
    }

    public String getSplitDurationString(String identifier) {
        return DurationFormatUtils.formatDurationHMS(getSplitDuration(identifier));
    }


    public String toString() {
        return getDurationString();
    }


}
